/*
 * ! Ext JS Library 3.0.0 Copyright(c) 2006-2009 Ext JS, LLC licensing@extjs.com
 * http://www.extjs.com/license
 */
(function() {
	Ext.ns('Ext.a11y');

	Ext.a11y.Frame = Ext.extend(Object, {
				initialized : false,

				constructor : function(size, color) {
					this.setSize(size || 1);
					this.setColor(color || '15428B');
				},

				init : function() {
					if (!this.initialized) {
						this.sides = [];

						var s, i;

						this.ct = Ext.DomHelper.append(document.body, {
									cls : 'x-a11y-focusframe'
								}, true);

						for (i = 0; i < 4; i++) {
							s = Ext.DomHelper.append(this.ct, {
										cls : 'x-a11y-focusframe-side',
										style : 'background-color: #'
												+ this.color
									}, true);
							s.visibilityMode = Ext.Element.DISPLAY;
							this.sides.push(s);
						}

						this.frameTask = new Ext.util.DelayedTask(function(el) {
									var newEl = Ext.get(el);
									if (newEl != this.curEl) {
										var w = newEl.getWidth();
										var h = newEl.getHeight();
										this.sides[0].show().setSize(w,
												this.size).anchorTo(el, 'tl',
												[0, -1]);
										this.sides[2].show().setSize(w,
												this.size).anchorTo(el, 'bl',
												[0, -1]);
										this.sides[1].show().setSize(this.size,
												h).anchorTo(el, 'tr', [-1, 0]);
										this.sides[3].show().setSize(this.size,
												h).anchorTo(el, 'tl', [-1, 0]);
										this.curEl = newEl;
									}
								}, this);

						this.unframeTask = new Ext.util.DelayedTask(function() {
									if (this.initialized) {
										this.sides[0].hide();
										this.sides[1].hide();
										this.sides[2].hide();
										this.sides[3].hide();
										this.curEl = null;
									}
								}, this);
						this.initialized = true;
					}
				},

				frame : function(el) {
					this.init();
					this.unframeTask.cancel();
					this.frameTask.delay(2, false, false, [el]);
				},

				unframe : function() {
					this.init();
					this.unframeTask.delay(2);
				},

				setSize : function(size) {
					this.size = size;
				},

				setColor : function(color) {
					this.color = color;
				}
			});

	Ext.a11y.FocusFrame = new Ext.a11y.Frame(2, '15428B');
	Ext.a11y.RelayFrame = new Ext.a11y.Frame(1, '6B8CBF');

	Ext.a11y.Focusable = Ext.extend(Ext.util.Observable, {
				constructor : function(el, relayTo, noFrame, frameEl) {
					Ext.a11y.Focusable.superclass.constructor.call(this);

					this.addEvents('focus', 'blur', 'left', 'right', 'up',
							'down', 'esc', 'enter', 'space');

					if (el instanceof Ext.Component) {
						this.el = el.el;
						this.setComponent(el);
					} else {
						this.el = Ext.get(el);
						this.setComponent(null);
					}

					this.setRelayTo(relayTo)
					this.setNoFrame(noFrame);
					this.setFrameEl(frameEl);

					this.init();

					Ext.a11y.FocusMgr.register(this);
				},

				init : function() {
					this.el.dom.tabIndex = '1';
					this.el.addClass('x-a11y-focusable');
					this.el.on({
								focus : this.onFocus,
								blur : this.onBlur,
								keydown : this.onKeyDown,
								scope : this
							});
				},

				setRelayTo : function(relayTo) {
					this.relayTo = relayTo
							? Ext.a11y.FocusMgr.get(relayTo)
							: null;
				},

				setNoFrame : function(noFrame) {
					this.noFrame = (noFrame === true) ? true : false;
				},

				setFrameEl : function(frameEl) {
					this.frameEl = frameEl && Ext.get(frameEl) || this.el;
				},

				setComponent : function(cmp) {
					this.component = cmp || null;
				},

				onKeyDown : function(e, t) {
					var k = e.getKey(), SK = Ext.a11y.Focusable.SpecialKeys, ret, tf;

					tf = (t !== this.el.dom)
							? Ext.a11y.FocusMgr.get(t, true)
							: this;
					if (!tf) {
						// this can happen when you are on a focused item within
						// a panel body
						// that is not a Ext.a11y.Focusable
						tf = Ext.a11y.FocusMgr.get(Ext.fly(t)
								.parent('.x-a11y-focusable'));
					}

					if (SK[k] !== undefined) {
						ret = this.fireEvent(SK[k], e, t, tf, this);
					}
					if (ret === false
							|| this.fireEvent('keydown', e, t, tf, this) === false) {
						e.stopEvent();
					}
				},

				focus : function() {
					this.el.dom.focus();
				},

				blur : function() {
					this.el.dom.blur();
				},

				onFocus : function(e, t) {
					this.el.addClass('x-a11y-focused');
					if (this.relayTo) {
						this.relayTo.el.addClass('x-a11y-focused-relay');
						if (!this.relayTo.noFrame) {
							Ext.a11y.FocusFrame.frame(this.relayTo.frameEl);
						}
						if (!this.noFrame) {
							Ext.a11y.RelayFrame.frame(this.frameEl);
						}
					} else {
						if (!this.noFrame) {
							Ext.a11y.FocusFrame.frame(this.frameEl);
						}
					}

					this.fireEvent('focus', e, t, this);
				},

				onBlur : function(e, t) {
					if (this.relayTo) {
						this.relayTo.el.removeClass('x-a11y-focused-relay');
						Ext.a11y.RelayFrame.unframe();
					}
					this.el.removeClass('x-a11y-focused');
					Ext.a11y.FocusFrame.unframe();
					this.fireEvent('blur', e, t, this);
				},

				destroy : function() {
					this.el.un('keydown', this.onKeyDown);
					this.el.un('focus', this.onFocus);
					this.el.un('blur', this.onBlur);
					this.el.removeClass('x-a11y-focusable');
					this.el.removeClass('x-a11y-focused');
					if (this.relayTo) {
						this.relayTo.el.removeClass('x-a11y-focused-relay');
					}
				}
			});

	Ext.a11y.FocusItem = Ext.extend(Object, {
				constructor : function(el, enableTabbing) {
					Ext.a11y.FocusItem.superclass.constructor.call(this);

					this.el = Ext.get(el);
					this.fi = new Ext.a11y.Focusable(el);
					this.fi.setComponent(this);

					this.fi.on('tab', this.onTab, this);

					this.enableTabbing = enableTabbing === true ? true : false;
				},

				getEnterItem : function() {
					if (this.enableTabbing) {
						var items = this.getFocusItems();
						if (items && items.length) {
							return items[0];
						}
					}
				},

				getFocusItems : function() {
					if (this.enableTabbing) {
						return this.el.query('a, button, input, select');
					}
					return null;
				},

				onTab : function(e, t) {
					var items = this.getFocusItems(), i;

					if (items && items.length && (i = items.indexOf(t)) !== -1) {
						if (e.shiftKey && i > 0) {
							e.stopEvent();
							items[i - 1].focus();
							Ext.a11y.FocusFrame.frame.defer(20,
									Ext.a11y.FocusFrame, [this.el]);
							return;
						} else if (!e.shiftKey && i < items.length - 1) {
							e.stopEvent();
							items[i + 1].focus();
							Ext.a11y.FocusFrame.frame.defer(20,
									Ext.a11y.FocusFrame, [this.el]);
							return;
						}
					}
				},

				focus : function() {
					if (this.enableTabbing) {
						var items = this.getFocusItems();
						if (items && items.length) {
							items[0].focus();
							Ext.a11y.FocusFrame.frame.defer(20,
									Ext.a11y.FocusFrame, [this.el]);
							return;
						}
					}
					this.fi.focus();
				},

				blur : function() {
					this.fi.blur();
				}
			});

	Ext.a11y.FocusMgr = function() {
		var all = new Ext.util.MixedCollection();

		return {
			register : function(f) {
				all.add(f.el && Ext.id(f.el), f);
			},

			unregister : function(f) {
				all.remove(f);
			},

			get : function(el, noCreate) {
				return all.get(Ext.id(el))
						|| (noCreate ? false : new Ext.a11y.Focusable(el));
			},

			all : all
		}
	}();

	Ext.a11y.Focusable.SpecialKeys = {};
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.LEFT] = 'left';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.RIGHT] = 'right';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.DOWN] = 'down';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.UP] = 'up';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ESC] = 'esc';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ENTER] = 'enter';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.SPACE] = 'space';
	Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.TAB] = 'tab';

	// we use the new observeClass method to fire our new initFocus method on
	// components
	Ext.util.Observable.observeClass(Ext.Component);
	Ext.Component.on('render', function(cmp) {
				cmp.initFocus();
				cmp.initARIA();
			});
	Ext.override(Ext.Component, {
				initFocus : Ext.emptyFn,
				initARIA : Ext.emptyFn
			});

	Ext.override(Ext.Container, {
				isFocusable : true,
				noFocus : false,

				// private
				initFocus : function() {
					if (!this.fi && !this.noFocus) {
						this.fi = new Ext.a11y.Focusable(this);
					}
					this.mon(this.fi, {
								focus : this.onFocus,
								blur : this.onBlur,
								tab : this.onTab,
								enter : this.onEnter,
								esc : this.onEsc,
								scope : this
							});

					if (this.hidden) {
						this.isFocusable = false;
					}

					this.on('show', function() {
								this.isFocusable = true;
							}, this);
					this.on('hide', function() {
								this.isFocusable = false;
							}, this);
				},

				focus : function() {
					this.fi.focus();
				},

				blur : function() {
					this.fi.blur();
				},

				enter : function() {
					var eitem = this.getEnterItem();
					if (eitem) {
						eitem.focus();
					}
				},

				onFocus : Ext.emptyFn,
				onBlur : Ext.emptyFn,

				onTab : function(e, t, tf) {
					var rf = tf.relayTo || tf;
					if (rf.component && rf.component !== this) {
						e.stopEvent();
						var item = e.shiftKey ? this
								.getPreviousFocus(rf.component) : this
								.getNextFocus(rf.component);
						item.focus();
					}
				},

				onEnter : function(e, t, tf) {
					// check to see if enter is pressed while "on" the panel
					if (tf.component && tf.component === this) {
						e.stopEvent();
						this.enter();
					}
					e.stopPropagation();
				},

				onEsc : function(e, t) {
					e.preventDefault();

					// check to see if esc is pressed while "inside" the panel
					// or while "on" the panel
					if (t === this.el.dom) {
						// "on" the panel, check if this panel has an owner
						// panel and focus that
						// we dont stop the event in this case so that this same
						// check will be
						// done for this ownerCt
						if (this.ownerCt) {
							this.ownerCt.focus();
						}
					} else {
						// we were inside the panel when esc was pressed,
						// so go back "on" the panel
						if (this.ownerCt && this.ownerCt.isFocusable) {
							var si = this.ownerCt.getFocusItems();

							if (si && si.getCount() > 1) {
								e.stopEvent();
							}
						}
						this.focus();
					}
				},

				getFocusItems : function() {
					return this.items && this.items.filterBy(function(o) {
								return o.isFocusable;
							}) || null;
				},

				getEnterItem : function() {
					var ci = this.getFocusItems(), length = ci
							? ci.getCount()
							: 0;

					if (length === 1) {
						return ci.first().getEnterItem
								&& ci.first().getEnterItem() || ci.first();
					} else if (length > 1) {
						return ci.first();
					}
				},

				getNextFocus : function(current) {
					var items = this.getFocusItems(), next = current, i = items
							.indexOf(current), length = items.getCount();

					if (i === length - 1) {
						next = items.first();
					} else {
						next = items.get(i + 1);
					}
					return next;
				},

				getPreviousFocus : function(current) {
					var items = this.getFocusItems(), prev = current, i = items
							.indexOf(current), length = items.getCount();

					if (i === 0) {
						prev = items.last();
					} else {
						prev = items.get(i - 1);
					}
					return prev;
				},

				getFocusable : function() {
					return this.fi;
				}
			});

	Ext.override(Ext.Panel, {
		/**
		 * @cfg {Boolean} enableTabbing <tt>true</tt> to enable tabbing.
		 *      Default is <tt>false</tt>.
		 */
		getFocusItems : function() {
			// items gets all the items inside the body
			var items = Ext.Panel.superclass.getFocusItems.call(this), bodyFocus = null;

			if (!items) {
				items = new Ext.util.MixedCollection();
				this.bodyFocus = this.bodyFocus
						|| new Ext.a11y.FocusItem(this.body, this.enableTabbing);
				items.add('body', this.bodyFocus);
			}
			// but panels can also have tbar, bbar, fbar
			if (this.tbar && this.topToolbar) {
				items.insert(0, this.topToolbar);
			}
			if (this.bbar && this.bottomToolbar) {
				items.add(this.bottomToolbar);
			}
			if (this.fbar) {
				items.add(this.fbar);
			}

			return items;
		}
	});

	Ext.override(Ext.TabPanel, {
				// private
				initFocus : function() {
					Ext.TabPanel.superclass.initFocus.call(this);
					this.mon(this.fi, {
								left : this.onLeft,
								right : this.onRight,
								scope : this
							});
				},

				onLeft : function(e) {
					if (!this.activeTab) {
						return;
					}
					e.stopEvent();
					var prev = this.items.itemAt(this.items
							.indexOf(this.activeTab)
							- 1);
					if (prev) {
						this.setActiveTab(prev);
					}
					return false;
				},

				onRight : function(e) {
					if (!this.activeTab) {
						return;
					}
					e.stopEvent();
					var next = this.items.itemAt(this.items
							.indexOf(this.activeTab)
							+ 1);
					if (next) {
						this.setActiveTab(next);
					}
					return false;
				}
			});

	Ext.override(Ext.tree.TreeNodeUI, {
				// private
				focus : function() {
					this.node.getOwnerTree().bodyFocus.focus();
				}
			});

	Ext.override(Ext.tree.TreePanel, {
				// private
				afterRender : function() {
					Ext.tree.TreePanel.superclass.afterRender.call(this);
					this.root.render();
					if (!this.rootVisible) {
						this.root.renderChildren();
					}
					this.bodyFocus = new Ext.a11y.FocusItem(this.body
							.down('.x-tree-root-ct'));
					this.bodyFocus.fi.setFrameEl(this.body);
				}
			});

	Ext.override(Ext.grid.GridPanel, {
				initFocus : function() {
					Ext.grid.GridPanel.superclass.initFocus.call(this);
					this.bodyFocus = new Ext.a11y.FocusItem(this.view.focusEl);
					this.bodyFocus.fi.setFrameEl(this.body);
				}
			});

	Ext.override(Ext.Button, {
				isFocusable : true,
				noFocus : false,

				initFocus : function() {
					Ext.Button.superclass.initFocus.call(this);
					this.fi = this.fi
							|| new Ext.a11y.Focusable(this.btnEl, null, null,
									this.el);
					this.fi.setComponent(this);

					this.mon(this.fi, {
								focus : this.onFocus,
								blur : this.onBlur,
								scope : this
							});

					if (this.menu) {
						this.mon(this.fi, 'down', this.showMenu, this);
						this.on('menuhide', this.focus, this);
					}

					if (this.hidden) {
						this.isFocusable = false;
					}

					this.on('show', function() {
								this.isFocusable = true;
							}, this);
					this.on('hide', function() {
								this.isFocusable = false;
							}, this);
				},

				focus : function() {
					this.fi.focus();
				},

				blur : function() {
					this.fi.blur();
				},

				onFocus : function() {
					if (!this.disabled) {
						this.el.addClass("x-btn-focus");
					}
				},

				onBlur : function() {
					this.el.removeClass("x-btn-focus");
				}
			});

	Ext.override(Ext.Toolbar, {
				initFocus : function() {
					Ext.Toolbar.superclass.initFocus.call(this);
					this.mon(this.fi, {
								left : this.onLeft,
								right : this.onRight,
								scope : this
							});

					this.on('focus', this.onButtonFocus, this, {
								stopEvent : true
							});
				},

				addItem : function(item) {
					Ext.Toolbar.superclass.add.apply(this, arguments);
					if (item.rendered && item.fi !== undefined) {
						item.fi.setRelayTo(this.el);
						this.relayEvents(item.fi, ['focus']);
					} else {
						item.on('render', function() {
									if (item.fi !== undefined) {
										item.fi.setRelayTo(this.el);
										this.relayEvents(item.fi, ['focus']);
									}
								}, this, {
									single : true
								});
					}
					return item;
				},

				onFocus : function() {
					var items = this.getFocusItems();
					if (items && items.getCount() > 0) {
						if (this.lastFocus
								&& items.indexOf(this.lastFocus) !== -1) {
							this.lastFocus.focus();
						} else {
							items.first().focus();
						}
					}
				},

				onButtonFocus : function(e, t, tf) {
					this.lastFocus = tf.component || null;
				},

				onLeft : function(e, t, tf) {
					e.stopEvent();
					this.getPreviousFocus(tf.component).focus();
				},

				onRight : function(e, t, tf) {
					e.stopEvent();
					this.getNextFocus(tf.component).focus();
				},

				getEnterItem : Ext.emptyFn,
				onTab : Ext.emptyFn,
				onEsc : Ext.emptyFn
			});

	Ext.override(Ext.menu.BaseItem, {
				initFocus : function() {
					this.fi = new Ext.a11y.Focusable(this, this.parentMenu
									&& this.parentMenu.el || null, true);
				}
			});

	Ext.override(Ext.menu.Menu, {
				initFocus : function() {
					this.fi = new Ext.a11y.Focusable(this);
					this.focusEl = this.fi;
				}
			});

	Ext.a11y.WindowMgr = new Ext.WindowGroup();

	Ext.apply(Ext.WindowMgr, {
				bringToFront : function(win) {
					Ext.a11y.WindowMgr.bringToFront.call(this, win);
					if (win.modal) {
						win.enter();
					} else {
						win.focus();
					}
				}
			});

	Ext.override(Ext.Window, {
				initFocus : function() {
					Ext.Window.superclass.initFocus.call(this);
					this.on('beforehide', function() {
								Ext.a11y.RelayFrame.unframe();
								Ext.a11y.FocusFrame.unframe();
							});
				}
			});

	Ext.override(Ext.form.Field, {
				isFocusable : true,
				noFocus : false,

				initFocus : function() {
					this.fi = this.fi
							|| new Ext.a11y.Focusable(this, null, true);

					Ext.form.Field.superclass.initFocus.call(this);

					if (this.hidden) {
						this.isFocusable = false;
					}

					this.on('show', function() {
								this.isFocusable = true;
							}, this);
					this.on('hide', function() {
								this.isFocusable = false;
							}, this);
				}
			});

	Ext.override(Ext.FormPanel, {
		initFocus : function() {
			Ext.FormPanel.superclass.initFocus.call(this);
			this.on('focus', this.onFieldFocus, this, {
						stopEvent : true
					});
		},

		// private
		createForm : function() {
			delete this.initialConfig.listeners;
			var form = new Ext.form.BasicForm(null, this.initialConfig);
			form.afterMethod('add', this.formItemAdd, this);
			return form;
		},

		formItemAdd : function(item) {
			item.on('render', function(field) {
						field.fi.setRelayTo(this.el);
						this.relayEvents(field.fi, ['focus']);
					}, this, {
						single : true
					});
		},

		onFocus : function() {
			var items = this.getFocusItems();
			if (items && items.getCount() > 0) {
				if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {
					this.lastFocus.focus();
				} else {
					items.first().focus();
				}
			}
		},

		onFieldFocus : function(e, t, tf) {
			this.lastFocus = tf.component || null;
		},

		onTab : function(e, t, tf) {
			if (tf.relayTo.component === this) {
				var item = e.shiftKey
						? this.getPreviousFocus(tf.component)
						: this.getNextFocus(tf.component);

				if (item) {
					ev.stopEvent();
					item.focus();
					return;
				}
			}
			Ext.FormPanel.superclass.onTab.apply(this, arguments);
		},

		getNextFocus : function(current) {
			var items = this.getFocusItems(), i = items.indexOf(current), length = items
					.getCount();

			return (i < length - 1) ? items.get(i + 1) : false;
		},

		getPreviousFocus : function(current) {
			var items = this.getFocusItems(), i = items.indexOf(current), length = items
					.getCount();

			return (i > 0) ? items.get(i - 1) : false;
		}
	});

	Ext.override(Ext.Viewport, {
				initFocus : function() {
					Ext.Viewport.superclass.initFocus.apply(this);
					this.mon(Ext.get(document), 'focus', this.focus, this);
					this.mon(Ext.get(document), 'blur', this.blur, this);
					this.fi.setNoFrame(true);
				},

				onTab : function(e, t, tf, f) {
					e.stopEvent();

					if (tf === f) {
						items = this.getFocusItems();
						if (items && items.getCount() > 0) {
							items.first().focus();
						}
					} else {
						var rf = tf.relayTo || tf;
						var item = e.shiftKey ? this
								.getPreviousFocus(rf.component) : this
								.getNextFocus(rf.component);
						item.focus();
					}
				}
			});

})();